package com.my.main.annotation.distributedLock;

import com.my.main.annotation.DistributedLockAnnotation;
import com.my.main.base.entity.response.ProfileResult;
import com.my.main.common.encrypt.DES3;
import com.my.main.common.utils.JSONUtil;
import com.my.main.entity.csApp.CsApp;
import com.my.main.service.csApp.CsAppService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class DistributedLockAnnotationAop {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CsAppService csAppService;
    /** 权限部分参数 */
    public ProfileResult userInfo;

    @Around("@annotation(com.my.main.annotation.DistributedLockAnnotation)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        //获取session中的安全数据
        Subject subject = SecurityUtils.getSubject();
        //1.subject获取所有的安全数据集合
        PrincipalCollection principals = subject.getPrincipals();
        if(principals != null && !principals.isEmpty()){
            //2.获取安全数据
            this.userInfo  = (ProfileResult)principals.getPrimaryPrincipal();
        }
        String userInfoForLock = "";
        if (userInfo != null) {
            userInfoForLock = userInfo.getAppKey() + userInfo.getUserId();
        }

        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        DistributedLockAnnotation distributedLock = method.getAnnotation(DistributedLockAnnotation.class);
        String lockKey = (String) DistributedLockAnnotationResolver.newInstance().resolver(joinPoint, distributedLock.lockKey());
        String failGetLockTip = (String) DistributedLockAnnotationResolver.newInstance().resolver(joinPoint, distributedLock.tip());

        // 可自定义规则
//        lockKey= "LOCK:"+method.getName()+":"+lockKey;
        if (!StringUtils.isEmpty(userInfoForLock) && userInfoForLock != "") {
            lockKey= "LOCK:" + userInfoForLock + ":" + method.getName() + ":" + lockKey;
        } else {
            // 第三方登录接口做单独处理
            if (method.getName().equals("thirdLogin")) {
                try {
                    String appId = (String) DistributedLockAnnotationResolver.newInstance().resolver(joinPoint, distributedLock.appId());
                    CsApp csApp = csAppService.queryStringId(appId);
                    String str = DES3.decrypt(lockKey, csApp.getAppSecret());
                    Map map = JSONUtil.parseMap(str);
                    if (csApp.getAppKey().equals("qy")) {
                        String mapStr = "qy" +  map.get("userId").toString() + map.get("userName").toString() + map.get("userPhone").toString();
                        lockKey = "LOCK:" + method.getName() + ":" + mapStr;
                    } else {
                        String mapStr = "oa" +  map.get("oaUserId").toString() + map.get("oaUserName").toString();
                        lockKey = "LOCK:" + method.getName() + ":" + mapStr;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                lockKey= "LOCK:" + method.getName() + ":" + lockKey;
            }
        }
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = false;
        try {
            locked = getLock(lock, lockKey);
            if (!(locked)) {
                log.info("已经有人占用了锁...");
                throw new InterruptedException(failGetLockTip);
            }
            log.info("获取到锁...,{}",lockKey);
            return joinPoint.proceed();
        } finally {
            try {
                if (locked) {
                    lock.unlock();
                }
            } catch (Exception ignore) {}
        }
    }

    private boolean getLock(RLock rLock, String lockKey) {
        try {
            // leaseTime: 锁有效时间 防止锁一直被占用
            // waitTime: 等待时间 没有获取到锁继续等待的时间
            return rLock.tryLock(1, 1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("Fail add lock,  key: [{}] ", lockKey, e);
            return false;
        }
    }

}
